%{
	#include <limits.h>
	
	#define SWAP(a,b,c) {c t;t=(a);(a)=(b);(b)=t;}
	#define MAX_SIZET ((size_t)(~(size_t)0)-2)
	#define MAX_INT (INT_MAX-2)
	#define brackets 2 //Es la cantidad de corchetes en un long bracket
	
	inline void printToken(char* id);
	inline void printError();
	inline void newLine();
	inline void increaseColumn();
	inline void SetBuffer();

	int n_lines = 1;
	int n_column = 0;
	int level, size = 0; //Auxiliar para saber el nivel del string y para saber el size
	char *buffer, *file;

	typedef enum {too_long, inc_string, inc_longstring, inc_longcomment, nnumber, long_code, nid} Errors;
%}

endl 		[\n\r]
whitespace	[ \t\v\f]+
escape		[\a\b\f{endl}\t\v]
varag		"..."
fieldsep 	[\;\,\:]
name		[_[:alpha:]][_[:alnum:]]*
bool		true|false
decimal 	[[:digit:]]+([eE][\-\+]?[[:digit:]]+)?|([[:digit:]]*\.[[:digit:]]+([eE][\-\+]?[[:digit:]]+)?)
hexadecimal	0[xX][[:xdigit:]]+(\.[[:xdigit:]]+)?([eEpP][\-\+]?[[:digit:]]+)?
number 		[\-]?({decimal}|{hexadecimal})

notxdigit	[[:alpha:]]-[[:xdigit:]]
notnumber	[\-]?(0[xX]{notxdigit}*(\.{notxdigit}*)?([pP]{notxdigit}*)?|[[:digit:]]*(\.[[:xdigit:]]*)([eE]([\-\+]?[[:digit:]]*[[:xdigit:]]*))?)

%x DQSTRING
%x QSTRING
%x LEVEL_STRING
%x LONG_COMMENT
%x SHORT_COMMENT
%%
	/*Espacios */
{endl}			newLine();
{whitespace}	increaseColumn();

	/* Palabras reservadas */
for				printToken("iter_for");
goto			printToken("iter_goto");
while			printToken("iter_while");
do				printToken("iter_do");
repeat			printToken("iter_repeat");
until			printToken("iter_until");
if 				printToken("cond_if");
then			printToken("cond_then");
else			printToken("cond_else");
elseif			printToken("cond_elseif");
function		printToken("rout_function");
break			printToken("rout_break");
return			printToken("rout_return");
local			printToken("rout_local");
end 			printToken("rout_end");
or				printToken("log_or");
and				printToken("log_and");
not				printToken("log_not");

	/* Operadores de comparación */
"<="		printToken("comp_leq");
"<"			printToken("comp_le");
">="		printToken("comp_geq");
">"			printToken("comp_gr");
"=="		printToken("comp_eq");
"~="		printToken("comp_neq");

	/* Operadores aritméticos */
"+"			printToken("op_mas");
"-"			printToken("op_menos");
"*"			printToken("op_mult");
"/"			printToken("op_div");
"^"			printToken("op_potencia");
"%"			printToken("op_mod");
"="			printToken("op_assign");
".."		printToken("op_concat");

	/* Otros operadores */
"#"			printToken("op_length"); /*Indica la longitud de algo*/
"."			printToken("op_dot");
"::"		printToken("op_scope");

	/* Separadores*/
{fieldsep}	printToken("fieldsep");

	/* Delimitadores */
\'			{ printToken("quote"); size = 0; BEGIN(QSTRING); }
\"			{ printToken("dquote"); size = 0; BEGIN(DQSTRING); }
\[=*\[		{ printToken("llongbra"); level = yyleng-brackets; size = 0; BEGIN(LEVEL_STRING); }
"--"		{ BEGIN(SHORT_COMMENT);	}
"--"\[=*\[	{ level = yyleng-(brackets<<1); BEGIN(LONG_COMMENT); }
"("			printToken("group_lpar");
")"			printToken("group_rpar");
"{"			printToken("group_lkey");
"}"			printToken("group_rkey");
"["			printToken("group_lbra");
"]"			printToken("group_rbra");

	/* Expresiones */
{bool}		printToken("exp_bool");
nil			printToken("exp_nil");
in			printToken("exp_in");
{number}	printToken("exp_number");
{name}		{
				if(yyleng >= MAX_SIZET/2)
					printError(too_long);
				else 
					printToken("exp_id");
			}
{varag}		printToken("exp_varag"); /*En el manual explican qué es varag */

	/*Errores */
{notnumber}			printError(nnumber);	
[[:digit:]]{name}	printError(nid);
	/*String que son delimitados por '  */
<QSTRING>
{
	{endl}			{ newLine(); SWAP(yytext, buffer, char*); printError(inc_string); BEGIN(0); }
	\'				{ 
						SWAP(yytext, buffer, char*);
						printToken("string");
						SWAP(yytext, buffer, char*);
						printToken("quote");
						BEGIN(0);
					}
	\\{endl}		{ newLine(); yytext++; yyleng--; SetBuffer(); }
	\\[\"\'\\\[\]]	|	
	\\[abfnrtv]		|
	.				{ SetBuffer(); }
}

	/*String que son delimitados por "  */
<DQSTRING>
{
	{endl}			{ newLine(); SWAP(yytext, buffer, char*); printError(inc_string); BEGIN(0); }
	\"				{ 
						SWAP(yytext, buffer, char*);
						printToken("string");
						SWAP(yytext, buffer, char*);
						printToken("dquote");
						BEGIN(0);
					}
	\\{endl}		{ newLine(); yytext++; yyleng--; SetBuffer(); }
	\\[\"\'\\\[\]]	|	
	\\[abfnrtv]		|
	.				{ SetBuffer(); }
}

	/*String con niveles */
<LEVEL_STRING>
{
	\]=*\]	{ 
				if(yyleng-brackets == level){
					SWAP(yytext, buffer, char*);
					printToken("string");
					SWAP(yytext, buffer, char*);
					printToken("rlongbra"); 
					BEGIN(0);
				}else
					SetBuffer();
			}
	<<EOF>>	{ printError(inc_longstring); BEGIN(0); }
	.		{ SetBuffer(); }
	{endl}	{ newLine(); SetBuffer(); }
}

	/*Comentario corto */
<SHORT_COMMENT>
{
	{endl}	{ newLine(); BEGIN(0); }
	.
}

	/*Comentario largo */
<LONG_COMMENT>
{
	{endl}	{ newLine(); }
	\]=*\]	{ 
				if(yyleng-(brackets<<1) == level){
					increaseColumn();
					BEGIN(0);
				}
			}
	<<EOF>>	{ printError(inc_longcomment); BEGIN(0); }
	.
}
%%
inline void SetBuffer(){
	sprintf(buffer+size, "%s", yytext);
	size += yyleng;
} 

inline void printToken(char* id)
{
	
	printf("%s:%s:%d\n", yytext, id, n_lines);
	increaseColumn();
}

inline void printError(Errors fail)
{
	increaseColumn();

	printf("%s: error lexico (%d): columna (%d): ", file, n_lines, n_column);
	switch(fail)
	{
		case too_long: printf("elemento lexico muy grande\n"); break;
		case inc_string: printf("string incompleto\n"); break;
		case inc_longstring: printf("long string incompleto, falta operador de cierre nivel %d\n", level); break;
		case inc_longcomment: printf("comentario incompleto, falta operador de cierre nivel %d\n", level); break;
		case nnumber: printf("numero malformado\n"); break;
		case long_code: printf("codigo excede cantidad de lineas permitidas\n"); break;
		case nid: printf("No es un id válido\n"); break;
		default: printf("\n");
	}
}

inline void newLine()
{
	n_lines++;
	n_column = 0;
	if(n_lines>=MAX_INT)
		printError(long_code);
}

inline void increaseColumn() { 
	n_column+= yyleng; 
}

int main (int argc, char *argv[])
{
	buffer = (char*)malloc(2048);
	file = (char*)malloc(255);
	if (argc > 2 || argc < 2)
	{	
		int k;

		for (k=1; k<argc; k++)
		{
			printf("%s ignorado\n", argv[k]);
		}

		printf("\nError en los parametros de entrada\n\n");

		return -1;
	}
	else
	{
		FILE *yyinput= fopen(argv[1], "r");

		if (!yyinput) 
		{
			printf("No se puede abrir %s\n", argv[1]);
			return -1;
		}
		else
		{
			// Le decimos al lexer que lea desde el archivo de entrada
			yyin = yyinput;
			file = argv[1]; //nombre del archivo!!
			// Y a trabajar!
			yylex();
		}
	}
	return 0;
}
